#include "esp_camera.h"
#include <WiFi.h>
#include <PubSubClient.h>

#include "auto_wifi.h"
#include "manual_wifi.h"
#include "mqtt.h"
#include "server.h"

#include "ntptime.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

#include "tft_screen.h"
//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
//            Ensure ESP32 Wrover Module or other board with PSRAM is selected
//            Partial images will be transmitted if image exceeds buffer size
//
//            You must select partition scheme from the board menu that has at least 3MB APP space.
//            Face Recognition is DISABLED for ESP32 and ESP32-S2, because it takes up from 15
//            seconds to process single frame. Face Detection is ENABLED if PSRAM is enabled as well

// ===================
// Select camera model
// ===================
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
//#define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
//#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
// ** Espressif Internal Boards **
//#define CAMERA_MODEL_ESP32_CAM_BOARD
//#define CAMERA_MODEL_ESP32S2_CAM_BOARD
//#define CAMERA_MODEL_ESP32S3_CAM_LCD

#include "camera_pins.h"

WiFiClient client;
PubSubClient mqttClient(client);
String mqttClient_id;
#define MSG_BUFFER_SIZE  (50)
char msg[MSG_BUFFER_SIZE];

const int timerInterval = 300000;    // time between each HTTP POST image
unsigned long previousMillis = 0;

const int FLASHLIGHT_PIN = 4; //闪光灯

bool camera_flag = false;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  Serial.begin(115200);
  Serial.println();
  /*
    Serial.print("正在连接 ");
    Serial.println(ssid);
    WiFi.mode(WIFI_STA);

    WiFi.begin(ssid, password);
    int wifiConnCount = 0;
    while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    wifiConnCount ++;
    if (wifiConnCount > WIFI_RETRY_COUNT) {
      Serial.println("Wifi连接超时，重试" + (String)wifiConnCount);
      delay(500);
      ESP.restart();
    }
    delay(1000);
    }
    Serial.println();
    Serial.println("连接Wifi成功");
  */
  //擦除原来的配置
  //wm.resetSettings();
  //delay(1000);
  // 初始化屏幕
  initTFT_Screen();
  Serial.print("正在配网...");
  delay(1000);
  printText("Connecting WIFI...", false, false);
  //启动配网
  wifi_setup();
  Serial.print("配网成功！");
  delay(1000);
  printText("Wifi Successed!", false, false);
  delay(1000);
  printText("Connecting MQTT...", false, false);
  initMQTT();
  // 连接MQTT服务
  connectMQTT();
  delay(1000);
  printText("Connecting NTP...", false, false);
  initNtp();

  pinMode(FLASHLIGHT_PIN, OUTPUT);

  Serial.println();
  Serial.print("ESP32-CAM IP Address: ");
  Serial.println(WiFi.localIP());

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;

  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {
    config.frame_size = FRAMESIZE_QVGA; // 320x240
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 32;  //0-63 lower number means higher quality
    config.fb_count = 1;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }
}

void loop() {

  tft_display();
  mqttClient.loop();
  /*
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= timerInterval) {
    previousMillis = currentMillis;
    printText("Warning , Detected Motion !", false, true);
    delay(3000);
    uploadPhoto();
    }
  */
}
void uploadPhoto() {
  String getAll = "";
  String getBody = "";
  digitalWrite(FLASHLIGHT_PIN, HIGH);
  delay(1000);
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  int cameraRetryCount = 0;
  while (!fb) {
    cameraRetryCount ++;
    if (cameraRetryCount > 3) {
      delay(1000);
      ESP.restart();
      return;
    }
    Serial.println("拍照失败，重试" + (String)cameraRetryCount);
    fb = esp_camera_fb_get();
    delay(1000);
  }
  Serial.println("茄子...！");
  delay(1000);
  digitalWrite(FLASHLIGHT_PIN, LOW);
  // 记录拍照时间，用于给图片命名
  loopNtp();
  String datetimeStr = timeData.localDate + "_" + timeData.localTime;
  Serial.println("ntp时间:" + datetimeStr);

  if (client.connected()) {
    Serial.println("图片服务器已连接");
  } else {
    Serial.println("图片服务器未连接...");
    Serial.println("正在连接图片服务器...");
  }
  printText("Connect to Server...", false, false);
  int serverRetryCount = 0;
  while (!client.connect(serverName.c_str(), serverPort, 10000)) {
    serverRetryCount ++;
    if (serverRetryCount > SERVER_RETRY_COUNT) {
      delay(1000);
      ESP.restart();
      return;
    }
    Serial.println("连接图片服务器失败，重试" + (String)serverRetryCount);
  }
  Serial.println("连接图片服务器成功！");
  printText("Connect Server Succeed !", false, false);

  String generateImageName = datetimeStr + ".jpg";
  String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"file\"; filename=\"" + generateImageName + "\"\r\nContent-Type: image/jpeg\r\n\r\n";
  String tail = "\r\n--RandomNerdTutorials--\r\n";

  uint16_t imageLen = fb->len;
  uint16_t extraLen = head.length() + tail.length();
  uint16_t totalLen = imageLen + extraLen;

  Serial.printf("imageLen:%d - extraLen: %d - totalLen: %d\n", imageLen, extraLen, totalLen);

  client.println("POST " + serverPath + " HTTP/1.1");
  client.println("Host: " + serverName);
  client.println("Content-Length: " + String(totalLen));
  client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
  client.println();
  client.print(head);

  uint8_t *fbBuf = fb->buf;
  size_t fbLen = fb->len;
  Serial.printf("fbLen: %d\n", fbLen);
  printText("Sending Image...", false, false);
  int count = 0;
  for (size_t n = 0; n < fbLen; n = n + 1024) {
    count++;
    Serial.printf("Sending part %d\n", count);
    if (n + 1024 < fbLen) {
      client.write(fbBuf, 1024);
      fbBuf += 1024;
    }
    else if (fbLen % 1024 > 0) {
      size_t remainder = fbLen % 1024;
      client.write(fbBuf, remainder);
    }
  }

  client.print(tail);

  esp_camera_fb_return(fb);

  unsigned long now = millis();
  boolean state = false;

  while (millis() - now < 20000) { // timeout 20 seconds
    Serial.print(".");
    delay(100);
    while (client.available()) {
      char c = client.read();
      if (c == '\n') {
        if (getAll.length() == 0) state = true;
        getAll = "";
      }
      else if (c != '\r') {
        getAll += String(c);
      }
      if (state == true) {
        getBody += String(c);
      }
    }
    if (getBody.length() > 0) break;
  }
  client.stop();
  Serial.println(getBody);
  delay(1000);

  printText("Connect to MQTT...", false, false);
  // 连接MQTT服务
  connectMQTT();
  printText("Uploading", false, false);
  mqttClient.publish(pubTopic, getBody.c_str(), true);
  Serial.println("发布成功！");
  printText("Upload Succeed !", false, false);

  delay(2000);
}

void initMQTT() {
  //init mqtt broker
  mqttClient.setServer(mqtt_broker, mqtt_port);
  mqttClient.setCallback(callback);
  mqttClient_id = "esp32-client-";
  mqttClient_id += String(WiFi.macAddress());
}

void connectMQTT() {
  Serial.println("正在连接MQTT服务...");
  int mqttRetryCount = 0;
  while (!mqttClient.connect(mqttClient_id.c_str(), mqtt_username, mqtt_password)) {
    mqttRetryCount ++;
    if (mqttRetryCount > MQTT_RETRY_COUNT) {
      delay(1000);
      ESP.restart();
      return;
    }
    Serial.println("连接MQTT失败，重试" + (String)mqttRetryCount);
  }
  Serial.printf("连接MQTT成功！ %s", mqttClient_id.c_str());
  Serial.println();
  // 订阅 & 发布
  mqttClient.subscribe(subTopic);
}

void callback(char *topic, byte *payload, unsigned int length) {

  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  String msg;
  for (int i = 0; i < length; i++) {
    //Serial.print((char) payload[i]);
    msg = msg + (char) payload[i];
  }
  Serial.println(msg);
  if (msg == "1") {
    printText("Warning , Detected Motion !", false, true);
    if (!camera_flag) {
      camera_flag = true;
      uploadPhoto();
      camera_flag = false;
    }
  }
}
